package com.yst.sqlbuilder;

import com.yst.lambda.TableFieldFunc;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

public class SQLCondition<T> implements ISQLExpressionGroup<T>, IParentedSelect<T> {
    private final IParentedSelect<T> parentSelect;
    protected SQLCondition<T> parentCondition;

    public SQLCondition(){
        this.parentCondition=null;
        this.parentSelect=null;
    }
    public SQLCondition(IParentedSelect<T> parentSelect){
        this.parentCondition=null;
        this.parentSelect=parentSelect;
    }
    public SQLCondition(SQLCondition<T> parentCondition,LinkMode linkMode){
        this.parentSelect=null;
        this.parentCondition=parentCondition;
        this.linkMode=linkMode;
    }
    protected SQLCondition<T> setParentCondition(SQLCondition<T> condition){
        this.parentCondition=condition;
        return this;
    }
    protected final List<ISQLExpression> expressionList=new ArrayList<>();
    public SQLCondition<T> compare(TableFieldFunc<T,?> field, Symbol symbol, Object value){
        expressionList.add(new SQLFieldCompareExp<>(this.parentSelect,field).setSymbol(symbol).setValue(value));
        return this;
    }
    public SQLCondition<T> equal(TableFieldFunc<T,?> field, Object value){
        return this.compare(field,Symbol.EQ,value);
    }
    public SQLCondition<T> notEqual(TableFieldFunc<T,?> field, Object value){
        return this.compare(field,Symbol.NOT_EQ,value);
    }
    public SQLCondition<T> less(TableFieldFunc<T,?> field, Object value){
        return this.compare(field,Symbol.LESS,value);
    }
    public SQLCondition<T> lessEqual(TableFieldFunc<T,?> field, Object value){
        return this.compare(field,Symbol.LESS_EQ,value);
    }
    public SQLCondition<T> more(TableFieldFunc<T,?> field, Object value){
        return this.compare(field,Symbol.MORE,value);
    }
    public SQLCondition<T> moreEqual(TableFieldFunc<T,?> field, Object value){
        return this.compare(field,Symbol.MORE_EQ,value);
    }
    @SafeVarargs
    public final SQLCondition<T> isNull(TableFieldFunc<T, ?>... fields){
        expressionList.addAll(Arrays.stream(fields).map((it)->new SQLFieldIsNull<>(it,getParentSelect())).collect(Collectors.toList()));
        return this;
    }
    @SafeVarargs
    public final SQLCondition<T> isNotNull(TableFieldFunc<T, ?>... fields){
        expressionList.addAll(Arrays.stream(fields).map((it)->new SQLFieldIsNotNull<>(it,getParentSelect())).collect(Collectors.toList()));
        return this;
    }
    public SQLCondition<T> in(TableFieldFunc<T,?> field, Collection<?> values){
        expressionList.add(new SQLFieldIn<>(getParentSelect(), field).setValues(values));
        return this;
    }
    public SQLCondition<T> between(TableFieldFunc<T,Number> field, Number value1, Number value2){
        expressionList.add(new SQLFieldBetween<>(field, getParentSelect(), value1, value2));
        return this;
    }
    public SQLCondition<T> between(TableFieldFunc<T,Date> field, Date value1, Date value2){
        expressionList.add(new SQLFieldBetween<>(field, getParentSelect(), value1, value2));
        return this;
    }

    public SQLCondition<T> between(TableFieldFunc<T,?> field, SQLParam<?> value1, SQLParam<?> value2){
        expressionList.add(new SQLFieldBetween<>(field, getParentSelect(), value1, value2));
        return this;
    }

    public SQLCondition<T> in(TableFieldFunc<T,?> field, Object...values){
        expressionList.add(new SQLFieldIn<>(getParentSelect(), field).setValues(Arrays.asList(values)));
        return this;
    }

    public SQLCondition<T> like(TableFieldFunc<T,String> field, String value){
        SQLFieldLike<T> sqlFieldLike=new SQLFieldLike<>(field,this.getParentSelect())
                .setLikeMode(SQLFieldLike.LikeMode.LM_ALL).setValue(value);
        expressionList.add(sqlFieldLike);
        return this;
    }
    public SQLCondition<T> likeLeft(TableFieldFunc<T,String> field, String value){
        SQLFieldLike<T> sqlFieldLike=new SQLFieldLike<>(field,this.getParentSelect())
                .setValue(value)
                .setLikeMode(SQLFieldLike.LikeMode.LM_LEFT);
        expressionList.add(sqlFieldLike);
        return this;
    }
    public SQLCondition<T> likeRight(TableFieldFunc<T,String> field, String value){
        SQLFieldLike<T> sqlFieldLike=new SQLFieldLike<>(field,this.getParentSelect())
                .setValue(value)
                .setLikeMode(SQLFieldLike.LikeMode.LM_RIGHT);
        expressionList.add(sqlFieldLike);
        return this;
    }
    @Override
    public String generateExpression(GenerateParam param) {
        if (expressionList.isEmpty()) return "";
        if (Objects.isNull(parentCondition)) {
            return "".concat(StringUtils.leftPad("", getLevel() * 2, " "))
                    .concat(expressionList.stream().map(it->it.generateExpression(param)).filter(cs->!StringUtils.isEmpty(cs))
                            .collect(Collectors.joining(" " + getLinkMode().expression + " ")));
        }
        return String.format("(%s)",""
                .concat(expressionList.stream().map(it->it.generateExpression(param)).filter(cs ->!StringUtils.isEmpty(cs))
                        .collect(Collectors.joining(" " + getLinkMode().expression + " "))));
    }

    /**
     * append a SQLCondition in this condition and return this
     * @param condition subCondition
     * @return this
     */
    public SQLCondition<T> append(SQLCondition<T> condition){
        expressionList.add(condition.setParentCondition(this));
        return this;
    }


    protected LinkMode linkMode=LinkMode.AND;

    public SQLCondition<T> setLinkMode(LinkMode linkMode){
        this.linkMode=linkMode;
        return this;
    }
    @Override
    public LinkMode getLinkMode() {
        return this.linkMode;
    }

    @Override
    public IParentedSelect<T> getParentSelect() {
        if (Objects.nonNull(parentCondition)){
            return parentCondition.getParentSelect();
        }
        return parentSelect;
    }

}
